#include <signal.h>
#include "Event.h"
#include "ExceptionManager.h"

EventVirtualTable* EventVirtualTableInstance = 0;

Event* EventNew(unsigned long pArgsCount, va_list* pParameters)
{
    Event* this;
    if(!(this = (Event*) malloc(sizeof(Event))))
    {
        raise(SIGABRT);
    }
    this->VirtualTable = EventInitialize(this, pArgsCount, pParameters);
    this->Constructor = EventConstructor;
    this->Destructor = EventDestructor;
    this->Clone = EventClone;
    this->AddDelegate = EventAddDelegate;
    this->RemoveDelegate = EventRemoveDelegate;
    this->FireEvent = EventFireEvent;
    return this;
}

EventVirtualTable* EventInitialize(Event* this, unsigned long pArgsCount, va_list* pParameters)
{
    if(!EventVirtualTableInstance)
    {
        EventVirtualTableInstance = (EventVirtualTable*) malloc(sizeof(EventVirtualTable));
        if(!EventVirtualTableInstance)
        {
            raise(SIGABRT);
        }

        EventVirtualTableInstance->TypeID = LIST_TYPEID;

        EventVirtualTableInstance->Interface = malloc(sizeof(EventInterface));
        if(!EventVirtualTableInstance->Interface)
        {
            raise(SIGABRT);
        }
        EventVirtualTableInstance->Interface->Constructor = EventConstructor;
        EventVirtualTableInstance->Interface->Destructor = EventDestructor;
        EventVirtualTableInstance->Interface->Clone = EventClone;
        EventVirtualTableInstance->Interface->AddDelegate = EventAddDelegate;
        EventVirtualTableInstance->Interface->RemoveDelegate = EventRemoveDelegate;
        EventVirtualTableInstance->Interface->FireEvent = EventFireEvent;

        EventVirtualTableInstance->ReferenceCount = 0;
        EventVirtualTableInstance->AncestorTable = ClassInitialize((Class*)this, pArgsCount, pParameters);

        EventVirtualTableInstance->Initialize = EventInitialize;
        EventVirtualTableInstance->Garbage = (void(*)(Event*))EventVirtualTableInstance->AncestorTable->Garbage;
        EventVirtualTableInstance->TypeOf = (unsigned long int (*)(Event*))EventVirtualTableInstance->AncestorTable->TypeOf;
        EventVirtualTableInstance->IsSameTypeThanTypeID = (bool (*)(Event*, unsigned long int))EventVirtualTableInstance->AncestorTable->IsSameTypeThanTypeID;
        EventVirtualTableInstance->IsKindOfTypeID = (bool (*)(Event*, unsigned long int))EventVirtualTableInstance->AncestorTable->IsKindOfTypeID;
        EventVirtualTableInstance->IsSameTypeThan = (bool (*)(Event*, Class*))EventVirtualTableInstance->AncestorTable->IsSameTypeThan;
        EventVirtualTableInstance->IsKindOf = (bool (*)(Event*, Class*))EventVirtualTableInstance->AncestorTable->IsKindOf;
    }
    EventVirtualTableInstance->Interface->Constructor(this, pArgsCount, pParameters);
    EventVirtualTableInstance->ReferenceCount++;
    return EventVirtualTableInstance;
}

void EventConstructor(Event* this, unsigned long int pArgsCount, va_list* pParameters)
{
    if(pArgsCount < 1)
    {
        ((ExceptionManager*)new((void*(*)(unsigned long int, va_list*))ExceptionManagerNew, 0))->throw(new((void*(*)(unsigned long int, va_list*))ExceptionNew, 1, "Event : Too few parameters for EventConstructor."));
    }
    this->aOwner = va_arg(pParameters, Class*);
    this->aDelegates = new((void* (*)(unsigned long int, va_list*))ListNew, 0);
}

void EventDestructor(Event* this)
{
    delete((void*)this->aDelegates);
}

Event* EventClone(Event* this)
{
    unsigned long int vIndex;
    Event* vClone = new ((void*(*)(unsigned long int, va_list*))EventNew, 1, this->aOwner);
    for(vIndex = 0; vIndex < this->aDelegates->Count(this->aDelegates); vIndex++)
    {
        vClone->AddDelegate(vClone, (EventDelegate*)this->aDelegates->Items(this->aDelegates, vIndex));
    }
    return vClone;
}

void EventAddDelegate(Event* this, EventDelegate* pDelegate)
{
    unsigned long int vDummy;
    if(!this->aDelegates->IsInList(this->aDelegates, &vDummy, (Class*)pDelegate))
    {
        this->aDelegates->Add(this->aDelegates, (Class*)pDelegate);
    }
}

void EventRemoveDelegate(Event* this, EventDelegate* pDelegate)
{
    unsigned long int vIndex = 0;
    if(this->aDelegates->IsInList(this->aDelegates, &vIndex, (Class*)pDelegate))
    {
        this->aDelegates->Remove(this->aDelegates, vIndex);
    }
}

void EventFireEvent(Event* this, EventArgs* pEventArgs)
{
    EventDelegate* vDelegate;
    unsigned long int vIndex;
    unsigned long int vCount = this->aDelegates->Count(this->aDelegates);
    for(vIndex = 0; vIndex < vCount; vIndex++)
    {
        vDelegate = (EventDelegate*)this->aDelegates->Items(this->aDelegates, vIndex);
        vDelegate->Handle(vDelegate, this->aOwner, pEventArgs);
    }
}

